// /////////////////////////////////////////////////////////////////////////////
//
// Name:            Entity.h
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Declaration of the Entity class.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

#ifndef __ENTITY_H__
#define __ENTITY_H__

// Include files
#include "../dependencies.h"
#include "AssetGroup.h"
#include "HasEvents.h"
#include "EntityComponent.h"
#include "Serializable.h"

// Forward declarations
class EntityComponent;


// Entity class
//! Represents an object in the game world.
//!
//! <b>XML:</b>
//! \code
//! <Entity name="" parent="">
//!    <!-- Properties -->
//!    <Components>
//!       <!-- Components -->
//!    </Components>
//! </Entity>
//! \endcode
class Entity : public HasEvents, public HasSlots, public Serializable
{
public:

    // Initialisation and deinitialisation
    //! Constructor
    //! @param name          Name of the entity.
    //! @param parent        Parent of the entity.
    //! @param group         Pointer to the AssetGroup that will be used as an alternative source
    //!                      for loading assets instead of the global unmanaged asset pool.
    Entity(const std::string &name, Entity *parent = NULL, AssetGroup *group = NULL);
    //! Deconstructor
    ~Entity();

    // Methods
    //! Gets the ID of this entity.
    u32 getID() const;
    //! Gets the name of this entity.
    const std::string& getName() const;

    //! Adds a child to the entity.
    //! @param entity     Pointer to the child to add.
    //! @return True if addition was successful, false if addition was a failure.
    bool addChild(Entity *entity);
    //! Adds a component to the entity.
    //! @param component     Pointer to the component to add.
    //! @return True if addition was successful, false if addition was a failure.
    bool addComponent(EntityComponent *component);

    //! Gets pointer to the AssetGroup that is linked to this entity.
    //! @return Pointer to the AssetGroup if available, else NULL.
    AssetGroup* getAssetGroup();

    //! Gets the child with the given ID from this entity.
    //! @return Pointer to the child if found, else NULL.
    Entity* getChild(const u32 id);
    //! Gets the child with the given name from this entity.
    //! @return Pointer to the child if found, else NULL.
    Entity* getChild(const std::string &name);
    //! Gets the component with the given ID from this entity.
    //! @return Pointer to the component if found, else NULL.
    EntityComponent* getComponent(const u32 id);
    //! Gets the component with the given name from this entity.
    //! @return Pointer to the component if found, else NULL.
    EntityComponent* getComponent(const std::string &name);

    //! Gets the parent of this entity.
    //! @return Pointer to the parent, NULL if there is no parent.
    Entity *getParent() const;

    //! Gets the absolute position of the entity.
    const vector3df& getAbsolutePosition() const;
    //! Gets the position of the entity.
    const vector3df& getPosition() const;
    //! Gets the absolute rotation of the entity.
    const vector3df& getAbsoluteRotation() const;
    //! Gets the rotation of the entity.
    const vector3df& getRotation() const;

    //! Removes all children.
    void removeChildren();
    //! Removes all components.
    void removeComponents();
    //! Removes the given child.
    //! @param entity        Pointer to the child to remove.
    //! @return True if removal was successful, false if removal was a failure.
    bool removeChild(Entity *entity);
    //! Removes the child with the given ID.
    //! @param id            ID of the child to remove.
    //! @return True if removal was successful, false if removal was a failure.
    bool removeChild(const u32 id);
    //! Removes the child with the given name.
    //! @param name          Name of the child to remove.
    //! @return True if removal was successful, false if removal was a failure.
    bool removeChild(const std::string &name);
    //! Removes the given component.
    //! @param component     Pointer to the component to remove.
    //! @return True if removal was successful, false if removal was a failure.
    bool removeComponent(EntityComponent *component);
    //! Removes the component with the given ID.
    //! @param id            ID of the component to remove.
    //! @return True if removal was successful, false if removal was a failure.
    bool removeComponent(const u32 id);
    //! Removes the component with the given name.
    //! @param name          Name of the component to remove.
    //! @return True if removal was successful, false if removal was a failure.
    bool removeComponent(const std::string &name);
    //! Removes the current parent of this entity.
    void removeParent();

    //! Sets the absolute position of the entity.
    //!
    //! <b>XML:</b>
    //! \code
    //! <absolutePosition x="" y="" z="" />
    //! \endcode
    //!
    //! @param position      New absolute position of the entity.
    void setAbsolutePosition(const vector3df &position);
    //! Sets the position of the entity.
    //!
    //! <b>XML:</b>
    //! \code
    //! <position x="" y="" z="" />
    //! \endcode
    //!
    //! @param position      New position of the entity.
    void setPosition(const vector3df &position);
    //! Sets the absolute rotation of the entity.
    //!
    //! <b>XML:</b>
    //! \code
    //! <absoluteRotation x="" y="" z="" />
    //! \endcode
    //!
    //! @param rotation      New absolute rotation of the entity.
    void setAbsoluteRotation(const vector3df &rotation);
    //! Sets the rotation of the entity.
    //!
    //! <b>XML:</b>
    //! \code
    //! <rotation x="" y="" z="" />
    //! \endcode
    //!
    //! @param rotation      New rotation of the entity.
    void setRotation(const vector3df &rotation);

    //! Sets the parent of the entity.
    //! @param parent        Parent of the entity.
    bool setParent(Entity *parent);

    // Events
    //! Updates the position of the entity after its parent has been updated.
    //! @note For internal use only!
    void onPositionChange(void *p);
    //! Updates the rotation of the entity after its parent has been updated.
    //! @note For internal use only!
    void onRotationChange(void *p);
    //! Updates the entity if the parent is updated.
    //! @note For internal use only!
    void onUpdate(void *p);
    //! Renders the entity if the parent is rendered.
    //! @note For internal use only!
    void onRender(void *p);
    //! Pauses the entity if the parent is paused.
    //! @note For internal use only!
    void onPause(void *p);
    //! Updates the entity if the parent is unpaused.
    //! @note For internal use only!
    void onUnPause(void *p);

    // Serialization
    //! Serializes this object.
    //! @param attributes
    virtual bool onSerialization(SerializedAttributes *attributes);
    //! Deserializes this object.
    //! @param attributes
    virtual bool onDeserialization(SerializedAttributes *attributes);

private:

    // Static members
    static u32 mIDCount;

    // Normal members
    Entity *pParent;
    AssetGroup *pAssetGroup;

    u32 mID;
    std::string mName;

    vector3df mPosition;
    vector3df mRotation;
    vector3df mAbsolutePosition;   // We store the absolute values too so that we don't have to
    vector3df mAbsoluteRotation;   // recalculate these values when we need them.

    bool mIsAddingChild;

    vector<Entity*> mChildren;
    vector<EntityComponent*> mComponents;
};

#endif
